﻿//#define DEBUG_CREATE_HEIGHT_DATA
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing;

namespace DirectX_CS
{
    public delegate float ZFuncOfXY(float x, float y);
    public delegate float FloatFunc(float t);		//Note: same as public delegate float FuncOfX(float x);

    public delegate float ZFuncOfTimeXY(float time, float x, float y);
		
    public class CartesianDataCreator: IColorable
    {

		private ZFuncOfXY mXYZFunc = null;
		private ZFuncOfTimeXY mZofTimeXY = null;
        private FloatFunc mXFunc = null;
        private FloatFunc mYFunc = null;
        private FloatFunc mZFunc = null;

		private double mTime = 0;
		[PGBrowsable]
		public double Time
		{
			get { return mTime; }
			set { mTime = value; }
		}

        protected SVector[] vertexData = null;
		[PGBrowsable]
		public VertexFormats VertexFormat
		{
			get { return SVector.Format; }
		}

        float tMin = 0, tMax = 10;
        float yMin = 0, yMax = 10;
        int divisionsX = 20;
		[PGBrowsable]
        public int DivisionsT
        {
            get { return divisionsX; }
            set { divisionsX = value; }
        }
        int divisionsY = 20;

        public CartesianDataCreator()
        {
            mXYZFunc = xyZFuncPlaneXY;
        }
        /// 
        public CartesianDataCreator(ZFuncOfXY zFunc)
        {
            mXYZFunc = zFunc;
        }
        public CartesianDataCreator(FloatFunc xFunc, FloatFunc yFunc, FloatFunc zFunc, float tmin, float tmax, int divt)
        {
            mXFunc = xFunc;
            mYFunc = yFunc;
            mZFunc = zFunc;
            tMin = tmin;
            tMax = tmax;
            DivisionsT = divt;
            divisionsY = 0;
        }
        public CartesianDataCreator(FloatFunc YFunc, FloatFunc ZFunc, float xmin, float xmax, int divx)
        {
            mXFunc = linetf;
            mYFunc = YFunc;
            mZFunc = ZFunc;
            tMin = xmin;
            tMax = xmax;
            //yMin = yMax = y;
            divisionsX = divx;
            DivisionsY = 0;
        }
        public CartesianDataCreator(FloatFunc ZFunc)
        {
            mXFunc = null;
            mYFunc = null;
            mZFunc = ZFunc;
            tMin = XMin;
            tMax = XMax;
            //yMin = yMax = y;
            //DivisionsY = 0;
        }
        public CartesianDataCreator(ZFuncOfXY zFunc, float xmin, float xmax, float ymin, float ymax, int divx, int divy)
        {
            mXYZFunc = zFunc;
            XMin = xmin;
            XMax = xmax;
            YMin = ymin;
            YMax = ymax;
            divisionsX = divx;
            divisionsY = divy;
        }

		public CartesianDataCreator(ZFuncOfTimeXY zFunc, float xmin, float xmax, float ymin, float ymax, int divx, int divy)
		{
			mZofTimeXY = zFunc;
			mXYZFunc = null;
			XMin = xmin;
			XMax = xmax;
			YMin = ymin;
			YMax = ymax;
			divisionsX = divx;
			divisionsY = divy;
		}
		[PGBrowsable]
        public ZFuncOfXY ZFunc
        {
            get { return mXYZFunc; }
            set { mXYZFunc = value; }
        }
		[PGBrowsable]
        public float XMin
        {
            get { return tMin; }
            set { tMin = value; }
        }
		[PGBrowsable]
        public float XMax
        {
            get { return tMax; }
            set { tMax = value; }
        }
		[PGBrowsable]
		public float YMin
        {
            get { return yMin; }
            set { yMin = value; }
        }
		[PGBrowsable]
        public float YMax
        {
            get { return yMax; }
            set { yMax = value; }
        }
		[PGBrowsable]
        public int DivisionsX
        {
            get { return divisionsX; }
            set { divisionsX = value; }
        }
		[PGBrowsable]
        public int DivisionsY
        {
            get { return divisionsY; }
            set { divisionsY = value; }
        }
		[PGBrowsable]
		Color mMainVertexColor = Color.IndianRed;
		public Color MainVertexColor
		{
			get { return mMainVertexColor; }
			set { mMainVertexColor = value; }
		}

		public Array GetVertices()
		{
			return (Array) vertexData;
		}
        public int GetVerticesCount
        {
            get { return vertexData.Length; }
        }
        /// <summary>
        /// Create the triangles	
        /// Make sure DivisionsX != 0 && DivisionsY != 0
        /// </summary>
        public void CreateVertexData()
        {
            float default_w = 1f;
            //Debug.Assert(DivisionsX >= 0 && DivisionsY >= 0);

            float DX = 1; //compiler complains
            if (DivisionsX > 0) 
                DX = (tMax - tMin) / DivisionsX;
            float DY = 1; //compiler complains
            if (DivisionsY > 0) 
                DY = (yMax - yMin) / DivisionsY;

            int EvalsX = DivisionsX + 1;
            int EvalsY = DivisionsY + 1;
            vertexData = new SVector[EvalsX * EvalsY];

            

            //for vertex types with texture coordinates member.
            //float u = 0f;
            //float v = 0f;

            int index = 0;
            float y;
            if (mYFunc != null)
                y = mYFunc(tMin);
            else
                y = yMin;
            float t = tMin;

            while (index < vertexData.Length)
            {
                //now save the generated point
                //float nx = (float)Math.Sqrt(1/3);
                //float ny = (float)Math.Sqrt(1/3);
                //float nz = (float)Math.Sqrt(1/3);

				int red = (int)(255 * Math.Abs((t - tMin) / (tMax - tMin)));
				int green = (int)(255 * Math.Abs((y - yMin) / (yMax - yMin)) );
				int blue = 0;// (int)(255 * (z - zMin) / (zMax - zMin));
				int alpha = 122;
				int color = Color.FromArgb(alpha, red, green, blue).ToArgb(); //mMainVertexColor.ToArgb();
                
                SVector positionV;
				if (mZofTimeXY != null)
				{
					positionV = new SVector(t, y, mZofTimeXY((float)mTime, t, y), 1, 1, 1, color); 
				}
				else if (mXYZFunc != null)
				{
					positionV = new SVector(t, y, mXYZFunc(t, y), 1, 1, 1, color);
				}
				else if (mXFunc != null)
				{
					if (mYFunc != null)
					{
						green = (int)(255 * (y - yMin) / (yMax - yMin));
						if (mZFunc != null)
							positionV = new SVector(mXFunc(t), mYFunc(t), mZFunc(t), 1, 1, 1, color);
						else
							positionV = new SVector(mXFunc(t), mYFunc(t), 0, 1, 1, 1, color);
					}
					else
					{
						if (mZFunc != null)
							positionV = new SVector(mXFunc(t), y, mZFunc(t), 1, 1, 1, color);
						else
							positionV = new SVector(mXFunc(t), y, 0, 1, 1, 1, color);
					}
				}
				else
				{
					if (mYFunc != null)
					{
						green = (int)(255 * (y - yMin) / (yMax - yMin));
						if (mZFunc != null)
							positionV = new SVector(t, mYFunc(t), mZFunc(t), 1, 1, 1, color);
						else
							positionV = new SVector(t, mYFunc(t), 0, 1, 1,1, color);
					}
					else
					{
						if (mZFunc != null)
							positionV = new SVector(t, 0, mZFunc(t), 1, 1, 1, color);
						else
							positionV = new SVector(t, 0, 0, 1, 1, 1, color);
					}
				}
                             

            #if DEBUG_CREATE_HEIGHT_DATA
                System.Diagnostics.Debug.Write(positionV.ToString() + "  ");
            #endif
                vertexData[index] = positionV;

                if (mYFunc == null && (index + 1) % (DivisionsX+1) == 0 && index != 0)
                {
                    y += DY;
                    t = tMin;
            #if DEBUG_CREATE_HEIGHT_DATA
                    System.Diagnostics.Debug.WriteLine("\nNewRow");
            #endif
                }
                else
                {
					red = (int)(255 * (t - tMin) / (tMax - tMin));
                    t += DX;
                }

                index++;
            } //end while loop

            ///BEGIN: BE SURE TO CALL... WHEN HAVE NEW DATA; OTHERWISE THE DATA WILL ONLY GET QUERIED ONCE.
            ///END: BE SURE TO CALL... AT END OF DATA CREATION. 
            ///
            
        }
        /// <summary>
        ///  Use row and column indices for ease of computation to an index in the array.
        ///   In other words there exists a function which maps one-to-one from (row,col) onto (index).
        /// </summary>
        /// <param name="row"> row >= 0</param>
        /// <param name="col"> col >= 0</param>
        public short VertexIndexFromRC(int row, int col)
        {
            //if (row < 0 || col < 0)
            //throw new Exception("Row or column index exception.");
            //short index = (short)(row*(DivisionsX + 1) + col);
            if (col < 0 || col >= (DivisionsX + 1))   
                throw new Exception("Data column exception: col " + col.ToString() + " out of bounds.");
            if (row < 0 || row >= (DivisionsY + 1))
                throw new Exception("Data row exception: row " + row.ToString() + " out of bounds.");

            short index = (short)(col + (row) * (DivisionsX + 1));
            return index;
        }

        public void DebugShowDataIndexMap()
        {
            System.Diagnostics.Debug.Write(">> ");

            for (short col = 0; col <= DivisionsX; col++)
                System.Diagnostics.Debug.Write(col.ToString() + " ");

            System.Diagnostics.Debug.WriteLine("");
            System.Diagnostics.Debug.WriteLine(">>>>>>>>");

            for (short j = 0; j <= DivisionsY; j++)
            {
                System.Diagnostics.Debug.Write(j.ToString() + "> ");
                for (short i = 0; i <= DivisionsX; i++)
                {
                    short index = VertexIndexFromRC(j, i);
                    System.Diagnostics.Debug.Write(index.ToString() + " ");
                }
                System.Diagnostics.Debug.WriteLine("");
            }
            System.Diagnostics.Debug.Flush();
        }

        private static float xyZFuncQuadratic(float x, float y, float a, float b, float c, float d, float e, float f)
        {
            return a*x*x + b*y*y+ c*x*y + d*x + e*y + - f;
        }
        public static float xyZFuncPlaneXY(float x, float y)
        {
            float cx = 1, cy = 1, d = -1;
            return cx * x + cy * y + d;
        }
        public static float xyZFuncSinCos(float x, float y)
        {
            return (float)(2 * Math.Sin(x) * Math.Cos(y));
        }
        public static float xyZFuncZero(float x, float y)
        {
            return 0;
        }
        public static float zerof(float t)
        {
            return 0;
        }
        public static float cosf(float f)
        {
            return (float)Math.Cos(f);
        }
        public static float sinf(float f)
        {
            return (float)Math.Sin(f);
        }
        public static float tanf(float f)
        {
            return (float)Math.Tan(f);
        }
        public static float linetf(float f)
        {
            return f;
        }
    }
}
